header{
#include <list>
#include <set>
#include <iostream>
#include <sstream>
#include <typeinfo>
#include <stdio.h>
#include <antlr/CharScanner.hpp>

using namespace std;
using namespace antlr;
}
options {
	language="Cpp";
    mangleLiteralPrefix = "KW_";
}

class PlantainParser extends Parser;

options {
    importVocab = Plantain;
	genHashLines = true;		// include line number information
	buildAST = true;			// uses CommonAST by default
    k=2;
}
tokens{
    APPLY;
    NIL;
    NOT;
    IS;
    NIS;
    NIDENTICALLY;
    NIN;
    TUPLE;
    LAMBDA;
    PARAMS;
    BODY;
        }

{
    protected:
    /** 
     * @requires comparenode is one of the "comparison" types of nodes
     * @returns a factory-created opposite comparison node to comparenode, 
     *   with the same text as comparenode
     */
    RefAST invert(RefAST comparenode){
        if (comparenode == nullAST){
            throw std::string("Tried to invert the null node");
        }
        switch(comparenode->getType()){
        case EQUAL:
            return astFactory->create(NEQ, comparenode->getText());
        case NEQ:
            return astFactory->create(EQUAL, comparenode->getText());
        case IS:
            return astFactory->create(NIS, comparenode->getText());
        case NIS:
            return astFactory->create(IS, comparenode->getText());
        case LESS:
            return astFactory->create(GEQ, comparenode->getText());
        case GEQ:
            return astFactory->create(LESS, comparenode->getText());
        case GREA:
            return astFactory->create(LEQ, comparenode->getText());
        case LEQ:
            return astFactory->create(GREA, comparenode->getText());
        case KW_identically:
            return astFactory->create(NIDENTICALLY, comparenode->getText());
        case NIDENTICALLY:
            return astFactory->create(KW_identically, comparenode->getText());
        case KW_in:
            return astFactory->create(NIN, comparenode->getText());
        case NIN:
            return astFactory->create(KW_in, comparenode->getText());
        default:
            throw std::string("This wasn't a compare node");
        }
    }

}

program
    :   progn  EOF
    ;

progn
    :   expr (SEMI! (expr)? )* 
    ;

block
    : LCURLY! progn RCURLY! {astFactory->makeASTRoot(currentAST, #[BODY, "body"]);}
    ;

parenexp!
    : LPAREN {#parenexp = #[NIL];} 
        (e:expr {#parenexp = #e;})? 
        RPAREN 
    ;

comparatorbase
    : (EQUAL|NEQ|LEQ|GEQ|LESS|GREA|KW_identically|KW_in)
    ;

comparator
    : (KW_is {#comparator = #[IS, "is"];} 
        (KW_not {#comparator = #[NIS, "nis"];})? 
        (base:comparatorbase {#comparator = ((#comparator==#[NIS]) ? #base : invert(#base));})?)
    | (KW_not base2:comparatorbase {#comparator = invert(#base2);})
    | base3:comparatorbase {#comparator = #base3;}
    ;

/*
listexp
    : LSQUARE tuple RSQUARE
    ;
*/

/*
 Operator precedence:

 Highest
 1: () {} [] grouping
 2: .
 3: funcall()
 4: * / %
 5: & 
 5.5: |
 6: + - 
 7: == != <= >= < > is (is not) identically in (not in)
 8: not
 9: and nand
 10: or nor xor
 9: , (tuples)
 10: as
 11: = 
 12: def install
 13: macros
 Lowest
*/
 
group
    : parenexp /*| listexp*/
    ;

lookup
    : (atom | group) (DOT^ atom)*
    ;

funcallexp!
    : fn:lookup {#funcallexp = #fn;}
        ( arg:parenexp {#funcallexp = #([APPLY], fn, arg);} )?
    ;

product
    : fact1:funcallexp ( (STAR^|DIV^|MOD^) fact2:funcallexp)*
    ;

bitwiseand
    : product (BITAND^ product)*
    ;

bitwiseor
    : bitwiseand ((BITXOR^|BITOR^) bitwiseand)*
    ;

sum 
    : bitwiseor ((PLUS^|MINUS^) bitwiseor)*
    ;

comparison 
    : left:sum {#comparison = #left;} 
        (comp:comparator right:sum {
                //cout<<"before "<<#comp->toStringTree()<<'|'<<#left->toStringTree()<<'|'<<#right->toStringTree()<<endl;
                #comparison = astFactory->create(#comp);
                #comparison->addChild(astFactory->create(#left));
                #comparison->addChild(astFactory->create(#right));
                //cout<<"after "<<#comparison->toStringTree()<<endl;
            })?
    ;

negation
    : comparison
    | KW_not body:comparison {#negation = #([NOT, "not"], body);}
    ;

booleanand
    : negation ((KW_and^ | KW_nand^) booleanand)?
    ;

booleanor
    : booleanand ((KW_or^ | KW_xor^ | KW_nor^) booleanor)?
    ;

tuple
    : first:booleanor 
        {
            bool deeper = false;
        }
        (COMMA! {
                if(!deeper){
                    astFactory->makeASTRoot(currentAST, #[TUPLE, ","]);
                    deeper = true;
                }
            }
            (rest:booleanor
                )?
        )*
    ;


paramlist
    : ID {astFactory->makeASTRoot(currentAST, #[PARAMS, "params"]);}
        (COMMA! ID)*
    ;


lambda
    : b1:block {#lambda = #([LAMBDA, "lambda"], [PARAMS, "params"], b1);}
    | KW_as  b1b:block {#lambda = #([LAMBDA, "lambda"], [PARAMS, "params"], b1b);}
    | KW_as
        LPAREN!
        (
            p2:paramlist //there are one or more params
            RPAREN!
            b2:block {
                #lambda = astFactory->create(#[LAMBDA, "lambda"]);
                #lambda->addChild(astFactory->dupList(#p2));
                //the body gets added automatically
                //#lambda->addChild(astFactory->dupList(#b2));
            }
        | 
            RPAREN! b2b:block {#lambda = #([LAMBDA, "lambda"], [PARAMS, "params"], b2b);} //there are no params
        ) 
    | KW_as 
        p3:paramlist 
        b3:block {
            #lambda = astFactory->create(#[LAMBDA, "lambda"]);
            #lambda->addChild(astFactory->dupList(#p3));
            //the body gets added automatically
            //#lambda->addChild(astFactory->dupList(#b3));
        } 
    ;

lambdaexpr
    : tuple
    | lambda
    ;

assignment
    : lambdaexpr (ASSIGN^ lambdaexpr)?
    ;


binding
    : assignment
    | KW_def^ ID assignment
    | KW_install^ ID assignment
    ;

macro
    : MACRO^ expr (macro | lambda)*
    | MACRO
    ;

macroexpr
    : binding
    | macro
    ;

/* temporary def as we go down the precedence chain */
expr
    : macroexpr
    ;

atom
    :  INT
    |  FLOAT
    |  ID
    ; 

